SPORE.$Import('core.array');
SPORE.$Import('core.string');
SPORE.$Import('core.number');
SPORE.$Import('core.function');

/*
---

name: JSON

description: JSON encoder and decoder.

license: MIT-style license.

See Also: <http://www.json.org/>

requires: [Array, String, Number, Function]

provides: JSON

...
*/

SPORE.register('core.json',function($ns){
	
	if (typeof JSON == 'undefined') this.JSON = {};

	var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
	
	var escape = function(chr){
		return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
	};
	
	JSON.validate = function(string){
		string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
						replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
						replace(/(?:^|:|,)(?:\s*\[)+/g, '');
	
		return (/^[\],:{}\s]*$/).test(string);
	};
	
	JSON.encode = JSON.stringify ? function(obj){
		return JSON.stringify(obj);
	} : function(obj){
		if (obj && obj.toJSON) obj = obj.toJSON();
	
		switch (typeOf(obj)){
			case 'string':
				return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
			case 'array':
				return '[' + obj.map(JSON.encode).clean() + ']';
			case 'object': case 'hash':
				var string = [];
				Object.each(obj, function(value, key){
					var json = JSON.encode(value);
					if (json) string.push(JSON.encode(key) + ':' + json);
				});
				return '{' + string + '}';
			case 'number': case 'boolean': return '' + obj;
			case 'null': return 'null';
		}
	
		return null;
	};
	
	JSON.decode = function(string, secure){
		if (!string || typeOf(string) != 'string') return null;
	
		if (secure || JSON.secure){
			if (JSON.parse) return JSON.parse(string);
			if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
		}
	
		return eval('(' + string + ')');
	};

});